package com.tianji.remark.service.impl;

import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.constant.RedisConstants;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.api.dto.remark.LikedTimesDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.remark.service.ILikedStatService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * <p>
 * 点赞记录表 服务实现类
 * 24.3.4这里直接使用redis实现了
 * </p>
 *
 * @author XinxuanZhuo
 * @since 2024-03-04
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LikedRecordServiceImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {

    // redis
    private final StringRedisTemplate redisTemplate;

    // 点赞统计
    private final ILikedStatService statService;

    /**
     * 点赞或者取消赞
     */
    @Override
    public void addLikeRecord(LikeRecordFormDTO recordDTO) {
        // 根据前端的参数执行 点赞 | 取消赞操作
        boolean doLikesOperationStatus = recordDTO.getLiked() ? doLike(recordDTO) : doUnLike(recordDTO);
        // 执行失败直接return
        if (!doLikesOperationStatus) {
            return;
        }
        // 执行成功统计当前业务的点赞总数 redis sCard
        Long totalLikes = redisTemplate.opsForSet().size(RedisConstants.LIKE_BIZ_KEY_PREFIX + recordDTO.getBizId());
        // 为null直接return
        if (totalLikes == null) {
            return;
        }

        // 保存当前业务点赞总数到redis，key是类型，filed是业务ID，值是点赞总数
        redisTemplate.opsForZSet().add(
                RedisConstants.LIKE_COUNT_KEY_PREFIX + recordDTO.getBizType(),
                recordDTO.getBizId().toString(),
                totalLikes
        );
    }

    /**
     * 根据业务ID批量查询点赞状态
     */
    @Override
    public Set<Long> isBizLiked(List<Long> bizIds) {
        // 获取当前用户id
        Long userId = UserContext.getUser();
        // 查询点赞状态 pipeline
        List<Object> objects = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            StringRedisConnection src = (StringRedisConnection) connection;
            // 查询点赞状态
            bizIds.forEach(
                    bizId -> {
                        String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + bizId;
                        src.sIsMember(key, userId.toString());
                    }
            );
            // https://blog.csdn.net/liyiyu123/article/details/105623679
            return null;
        });
        // 返回结果
        return IntStream.range(0, objects.size())
                .filter(index -> (boolean) objects.get(index))
                // 对保留的索引应用 mapToObj 方法，从 bizIds 列表中取出相应索引位置上的元素。
                .mapToObj(bizIds::get)
                .collect(Collectors.toSet());
    }

    /**
     * 点赞总数变更任务
     */
    @Override
    public void readLikedTimesAndSendMessage(String bizType, int maxBizSize) {
        // 读取并移除Redis中缓存的点赞总数
        String key = RedisConstants.LIKE_COUNT_KEY_PREFIX + bizType;
        // 每次弹出30最小（排序）的元组【弹出且删除】
        Set<ZSetOperations.TypedTuple<String>> tuples = redisTemplate.opsForZSet().popMin(key, maxBizSize);
        if (CollUtils.isEmpty(tuples)) {
            return;
        }
        // 数据转换
        List<LikedTimesDTO> list = new ArrayList<>(tuples.size());
        for (ZSetOperations.TypedTuple<String> tuple: tuples){
            String bizId = tuple.getValue();
            Double likedTimes = tuple.getScore();
            if (bizId == null || likedTimes == null) {
                continue;
            }
            list.add(LikedTimesDTO.of(Long.valueOf(bizId), likedTimes.intValue()));
        }

        // 批量修改点赞数量
        statService.updateLikedTimes(bizType, list);

    }

    /**
     * 用户取消赞操作
     */
    private boolean doUnLike(LikeRecordFormDTO dto) {
        // 获取当前用户
        Long userId = UserContext.getUser();
        // 拼接key
        String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + dto.getBizId();
        // redis SREM http://doc.redisfans.com/set/srem.html
        Long remove = redisTemplate.opsForSet().remove(key, userId.toString());
        return remove != null && remove > 0;
    }

    /**
     * 用户点赞操作
     */
    private boolean doLike(LikeRecordFormDTO dto) {
        // 获取当前用户
        Long userId = UserContext.getUser();
        // 拼接key
        String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + dto.getBizId();
        Long add = redisTemplate.boundSetOps(key).add(userId.toString());
        // redis SADD
        return add != null && add > 0;
    }


}
